/********************************** (C) COPYRIGHT *******************************
* File Name          : crc16.c
* Author             : nx666
* Version            : V1.0.0
* Date               : 2023/07/22
* Description        : 实现crc16
*******************************************************************************/
/***********************************头文件区域***********************************/
#include "crc16.h"
#include "common_type.h"

/***********************************类型定义*********************************/
typedef struct{
    uInt16 wCRCin;         //算法开始时寄存器预置值，计算结束也保存crc值
    uInt16 wCPoly;         //生成项简写，如 x16+x12+x5+1 忽略高位简写为（0x1021）
    uInt16 wResultXOR;      //结果异或值
    uInt8 isInputInvert;    //输入是否反转
    uInt8 isOuputInvert;    //输出是否反转
    uInt8  *data;            //输入数据
    Int32 dataLen;          //输入数据长度
}crc16_;

/***********************************宏定义区***********************************/


/******************************** *全局变量区**********************************/

/*
CRC算法参数模型解释： 
    NAME：参数模型名称。 
    WIDTH：宽度，即CRC比特数。 
    POLY：生成项的简写，以16进制表示。例如：CRC-32即是0x04C11DB7，忽略了最高位的"1"，即完整的生    成项是0x104C11DB7。 
    INIT：这是算法开始时寄存器（crc）的初始化预置值，十六进制表示。 
    REFIN：待测数据的每个字节是否按位反转，True或False。 
    REFOUT：在计算后之后，异或输出之前，整个数据是否按位反转，True或False。 
    XOROUT：计算结果与此参数异或后得到最终的CRC值。
    Alias：别名及其应用范围。
*/

/*********************************************************************
 * @fn      crc16_common
 *
 * @brief   实际执行crc的函数
 *          pCrc16 输入待处理的的数据
 *
 * @return  0,失败   非0，成功
 *          
 */
static uInt16 crc16_common(crc16_ *pCrc16)
{
    if (!pCrc16)
    {
        return 0;
    }
    uInt8 wChar = 0;
    Int32 dataLen = pCrc16->dataLen;
    uInt8 *pData = pCrc16->data;
    while (dataLen--)
    {
        wChar = *(pData++);
        //输入值反转
        if(pCrc16->isInputInvert)
        {
            uInt8 temp_char = wChar;
            wChar=0;
            for(int i=0;i<8;++i)
            {
                if(temp_char&0x01)
                {
                    wChar|=0x01<<(7-i);
                }
                temp_char>>=1;
            }
        }
        //数据与CRCIn高8位异或
        pCrc16->wCRCin ^= (wChar << 8);
        for (int i = 0; i < 8; i++)
        {
            //高位为1则左移一位再与多项式Hex码异或。
            if (pCrc16->wCRCin & 0x8000)
            {
                pCrc16->wCRCin = (pCrc16->wCRCin << 1) ^ pCrc16->wCPoly;
            }
            else //高位为0则直接左移
            {
                pCrc16->wCRCin = pCrc16->wCRCin << 1;
            }
        }
    }

    //输出数据反转
    if(pCrc16->isOuputInvert)
    {
        uInt16 temp_short = pCrc16->wCRCin;
        pCrc16->wCRCin = 0;
        for(int i=0;i<16;++i)
        {
            if(temp_short&0x01)
            {
                pCrc16->wCRCin |= 0x01<<(15-i);
            }
            temp_short>>=1;
        }
    }

    //输出按位异或
    pCrc16->wCRCin ^= pCrc16->wResultXOR;
    return (pCrc16->wCRCin);
}

/****************************Info********************************************** 
 * Name:    CRC-16/CCITT        x16+x12+x5+1 
 * Width:	16
 * Poly:    0x1021 
 * Init:    0x0000 
 * Refin:   True 
 * Refout:  True 
 * Xorout:  0x0000 
 * Alias:   CRC-CCITT,CRC-16/CCITT-TRUE,CRC-16/KERMIT 
 *****************************************************************************/ 
uInt16 CRC16_CCITT(uInt8 *data, int datalen)
{
    crc16_ crc16;
    crc16.data = data;
    crc16.dataLen = datalen;
    crc16.wCPoly = 0x1021;
    crc16.wCRCin = 0x0000;
    crc16.isInputInvert = 1;
    crc16.isOuputInvert = 1;
    crc16.wResultXOR = 0x0000;

    return crc16_common(&crc16);
}

/****************************Info********************************************** 
 * Name:    CRC-16/CCITT-FALSE   x16+x12+x5+1 
 * Width:	16 
 * Poly:    0x1021 
 * Init:    0xFFFF 
 * Refin:   False 
 * Refout:  False 
 * Xorout:  0x0000 
 * Note: 
 *****************************************************************************/ 
uInt16 CRC16_CCITT_FALSE(uInt8 *data, int datalen)
{
    crc16_ crc16;
    crc16.data = data;
    crc16.dataLen = datalen;
    crc16.wCPoly = 0x1021;
    crc16.wCRCin = 0xFFFF;
    crc16.isInputInvert = 0;
    crc16.isOuputInvert = 0;
    crc16.wResultXOR = 0x0000;

    return crc16_common(&crc16);
}

/****************************Info********************************************** 
 * Name:    CRC-16/XMODEM       x16+x12+x5+1 
 * Width:	16 
 * Poly:    0x1021 
 * Init:    0x0000 
 * Refin:   False 
 * Refout:  False 
 * Xorout:  0x0000 
 * Alias:   CRC-16/ZMODEM,CRC-16/ACORN 
 *****************************************************************************/ 
uInt16 CRC16_XMODEM(uInt8 *data, int datalen)
{
    crc16_ crc16;
    crc16.data = data;
    crc16.dataLen = datalen;
    crc16.wCPoly = 0x1021;
    crc16.wCRCin = 0x0000;
    crc16.isInputInvert = 0;
    crc16.isOuputInvert = 0;
    crc16.wResultXOR = 0x0000;

    return crc16_common(&crc16);
}

/****************************Info********************************************** 
 * Name:    CRC-16/X25          x16+x12+x5+1 
 * Width:	16 
 * Poly:    0x1021 
 * Init:    0xFFFF 
 * Refin:   True 
 * Refout:  True 
 * Xorout:  0XFFFF 
 * Note: 
 *****************************************************************************/
uInt16 CRC16_X25(uInt8 *data, int datalen)
{
    crc16_ crc16;
    crc16.data = data;
    crc16.dataLen = datalen;
    crc16.wCPoly = 0x1021;
    crc16.wCRCin = 0xFFFF;
    crc16.isInputInvert = 1;
    crc16.isOuputInvert = 1;
    crc16.wResultXOR = 0xFFFF;

    return crc16_common(&crc16);
}

/****************************Info********************************************** 
 * Name:    CRC-16/MODBUS       x16+x15+x2+1 
 * Width:	16 
 * Poly:    0x8005 
 * Init:    0xFFFF 
 * Refin:   True 
 * Refout:  True 
 * Xorout:  0x0000 
 * Note: 
 *****************************************************************************/
uInt16 CRC16_MODBUS(uInt8 *data, int datalen)
{
    crc16_ crc16;
    crc16.data = data;
    crc16.dataLen = datalen;
    crc16.wCPoly = 0x8005;
    crc16.wCRCin = 0xFFFF;
    crc16.isInputInvert = 1;
    crc16.isOuputInvert = 1;
    crc16.wResultXOR = 0x0000;

    return crc16_common(&crc16);
}


/****************************Info********************************************** 
 * Name:    CRC-16/DNP          x16+x13+x12+x11+x10+x8+x6+x5+x2+1 
 * Width:	16 
 * Poly:    0x3D65 
 * Init:    0x0000 
 * Refin:   True 
 * Refout:  True 
 * Xorout:  0xFFFF 
 * Use:     M-Bus,ect. 
 *****************************************************************************/  
uInt16 CRC16_DNP(uInt8 *data, int datalen)
{
    crc16_ crc16;
    crc16.data = data;
    crc16.dataLen = datalen;
    crc16.wCPoly = 0x3D65;
    crc16.wCRCin = 0x0000;
    crc16.isInputInvert = 1;
    crc16.isOuputInvert = 1;
    crc16.wResultXOR = 0xFFFF;

    return crc16_common(&crc16);
}

/****************************Info********************************************** 
 * Name:    CRC-16/USB          x16+x15+x2+1 
 * Width:	16 
 * Poly:    0x8005 
 * Init:    0xFFFF 
 * Refin:   True 
 * Refout:  True 
 * Xorout:  0xFFFF 
 * Note: 
 *****************************************************************************/ 
uInt16 CRC16_USB(uInt8 *data, int datalen)
{
    crc16_ crc16;
    crc16.data = data;
    crc16.dataLen = datalen;
    crc16.wCPoly = 0x8005;
    crc16.wCRCin = 0xFFFF;
    crc16.isInputInvert = 1;
    crc16.isOuputInvert = 1;
    crc16.wResultXOR = 0xFFFF;

    return crc16_common(&crc16);
}

/****************************Info********************************************** 
 * Name:    CRC-16/IBM          x16+x15+x2+1 
 * Width:	16 
 * Poly:    0x8005 
 * Init:    0x0000 
 * Refin:   True 
 * Refout:  True 
 * Xorout:  0x0000 
 * Alias:   CRC-16,CRC-16/ARC,CRC-16/LHA 
 *****************************************************************************/ 
uInt16 CRC16_IBM(uInt8 *data, int datalen)
{
    crc16_ crc16;
    crc16.data = data;
    crc16.dataLen = datalen;
    crc16.wCPoly = 0x8005;
    crc16.wCRCin = 0x0000;
    crc16.isInputInvert = 1;
    crc16.isOuputInvert = 1;
    crc16.wResultXOR = 0x0000;

    return crc16_common(&crc16);
}


/****************************Info********************************************** 
 * Name:    CRC-16/MAXIM        x16+x15+x2+1 
 * Width:	16 
 * Poly:    0x8005 
 * Init:    0x0000 
 * Refin:   True 
 * Refout:  True 
 * Xorout:  0xFFFF 
 * Note: 
 *****************************************************************************/
uInt16 CRC16_MAXIM(uInt8 *data, int datalen)
{
    crc16_ crc16;
    crc16.data = data;
    crc16.dataLen = datalen;
    crc16.wCPoly = 0x8005;
    crc16.wCRCin = 0x0000;
    crc16.isInputInvert = 1;
    crc16.isOuputInvert = 1;
    crc16.wResultXOR = 0xFFFF;

    return crc16_common(&crc16);
}